#pragma once

#include <d3d9.h>
#include "LokiFakeUnknown.h"

#define DECLARE_METHOD_IO(method_) \
	std::vector<method_##Inputs> _method_##Inputs; \
	std::vector<method_##Outputs> _method_##Outputs; \
	int _method_##OutputCount

class LokiFakeDirect3D9 : public LokiFakeUnknown, public IDirect3D9
{
public:
	static LokiFakeDirect3D9 *GetInstance();
	virtual ~LokiFakeDirect3D9()
	{}

    /*** IUnknown methods ***/
    STDMETHOD(QueryInterface)(REFIID riid, void** ppvObj);
    STDMETHOD_(ULONG,AddRef)();
    STDMETHOD_(ULONG,Release)();

    /*** IDirect3D9 methods ***/
    STDMETHOD(RegisterSoftwareDevice)(void* pInitializeFunction);
	typedef Loki::Tuple<LOKI_TYPELIST_1(void *)> RegisterSoftwareDeviceInputs;
	typedef Loki::Tuple<LOKI_TYPELIST_1(HRESULT)> RegisterSoftwareDeviceOutputs;

	STDMETHOD_(UINT, GetAdapterCount)();
	typedef Loki::Tuple<LOKI_TYPELIST_1(UINT)> GetAdapterCountOutputs;

    STDMETHOD(GetAdapterIdentifier)(UINT Adapter,DWORD Flags,D3DADAPTER_IDENTIFIER9* pIdentifier);
	typedef Loki::Tuple<LOKI_TYPELIST_3(UINT, DWORD, D3DADAPTER_IDENTIFIER9 *)> GetAdapterIdentifierInputs;
	typedef Loki::Tuple<LOKI_TYPELIST_2(D3DADAPTER_IDENTIFIER9, HRESULT)> GetAdapterIdentifierOutputs;

    STDMETHOD_(UINT, GetAdapterModeCount)(UINT Adapter,D3DFORMAT Format);
	typedef Loki::Tuple<LOKI_TYPELIST_2(UINT, D3DFORMAT)> GetAdapterModeCountInputs;
	typedef Loki::Tuple<LOKI_TYPELIST_1(UINT)> GetAdapterModeCountOutputs;

    STDMETHOD(EnumAdapterModes)(UINT Adapter,D3DFORMAT Format,UINT Mode,D3DDISPLAYMODE* pMode);
	typedef Loki::Tuple<LOKI_TYPELIST_4(UINT, D3DFORMAT, UINT, D3DDISPLAYMODE *)> EnumAdapterModesInputs;
	typedef Loki::Tuple<LOKI_TYPELIST_2(D3DDISPLAYMODE, HRESULT)> EnumAdapterModesOutputs;

    STDMETHOD(GetAdapterDisplayMode)(UINT Adapter,D3DDISPLAYMODE* pMode);
	typedef Loki::Tuple<LOKI_TYPELIST_2(UINT, D3DDISPLAYMODE *)> GetAdapterDisplayModeInputs;
	typedef Loki::Tuple<LOKI_TYPELIST_2(D3DDISPLAYMODE, HRESULT)> GetAdapterDisplayModeOutputs;

    STDMETHOD(CheckDeviceType)(UINT Adapter,D3DDEVTYPE DevType,D3DFORMAT AdapterFormat,D3DFORMAT BackBufferFormat,BOOL bWindowed);
	typedef Loki::Tuple<LOKI_TYPELIST_5(UINT, D3DDEVTYPE, D3DFORMAT, D3DFORMAT, BOOL)> CheckDeviceTypeInputs;
	typedef Loki::Tuple<LOKI_TYPELIST_1(HRESULT)> CheckDeviceTypeOutputs;

    STDMETHOD(CheckDeviceFormat)(UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT AdapterFormat,DWORD Usage,D3DRESOURCETYPE RType,D3DFORMAT CheckFormat);
	typedef Loki::Tuple<LOKI_TYPELIST_6(UINT, D3DDEVTYPE, D3DFORMAT, DWORD, D3DRESOURCETYPE, D3DFORMAT)> CheckDeviceFormatInputs;
	typedef Loki::Tuple<LOKI_TYPELIST_1(HRESULT)> CheckDeviceFormatOutputs;

    STDMETHOD(CheckDeviceMultiSampleType)(UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT SurfaceFormat,BOOL Windowed,D3DMULTISAMPLE_TYPE MultiSampleType,DWORD* pQualityLevels);
	typedef Loki::Tuple<LOKI_TYPELIST_6(UINT, D3DDEVTYPE, D3DFORMAT, BOOL, D3DMULTISAMPLE_TYPE, DWORD *)> CheckDeviceMultiSampleTypeInputs;
	typedef Loki::Tuple<LOKI_TYPELIST_2(DWORD, HRESULT)> CheckDeviceMultiSampleTypeOutputs;

    STDMETHOD(CheckDepthStencilMatch)(UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT AdapterFormat,D3DFORMAT RenderTargetFormat,D3DFORMAT DepthStencilFormat);
	typedef Loki::Tuple<LOKI_TYPELIST_5(UINT, D3DDEVTYPE, D3DFORMAT, D3DFORMAT, D3DFORMAT)> CheckDepthStencilMatchInputs;
	typedef Loki::Tuple<LOKI_TYPELIST_1(HRESULT)> CheckDepthStencilMatchOutputs;

    STDMETHOD(CheckDeviceFormatConversion)(UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT SourceFormat,D3DFORMAT TargetFormat);
	typedef Loki::Tuple<LOKI_TYPELIST_4(UINT, D3DDEVTYPE, D3DFORMAT, D3DFORMAT)> CheckDeviceFormatConversionInputs;
	typedef Loki::Tuple<LOKI_TYPELIST_1(HRESULT)> CheckDeviceFormatConversionOutputs;

	STDMETHOD(GetDeviceCaps)(UINT Adapter,D3DDEVTYPE DeviceType,D3DCAPS9* pCaps);
	typedef Loki::Tuple<LOKI_TYPELIST_3(UINT, D3DDEVTYPE, D3DCAPS9 *)> GetDeviceCapsInputs;
	typedef Loki::Tuple<LOKI_TYPELIST_2(D3DCAPS9, HRESULT)> GetDeviceCapsOutputs;

    STDMETHOD_(HMONITOR, GetAdapterMonitor)(UINT Adapter);
	typedef Loki::Tuple<LOKI_TYPELIST_1(UINT)> GetAdapterMonitorInputs;
	typedef Loki::Tuple<LOKI_TYPELIST_1(HMONITOR)> GetAdapterMonitorOutputs;

    STDMETHOD(CreateDevice)(UINT Adapter,D3DDEVTYPE DeviceType,HWND hFocusWindow,DWORD BehaviorFlags,D3DPRESENT_PARAMETERS* pPresentationParameters,IDirect3DDevice9** ppReturnedDeviceInterface);
	typedef Loki::Tuple<LOKI_TYPELIST_6(UINT, D3DDEVTYPE, HWND, DWORD, D3DPRESENT_PARAMETERS *, IDirect3DDevice9 **)> CreateDeviceInputs;
	typedef Loki::Tuple<LOKI_TYPELIST_2(IDirect3DDevice9 *, HRESULT)> CreateDeviceOutputs;

private:
	LokiFakeDirect3D9();

	std::vector<RegisterSoftwareDeviceInputs> _registerSoftwareDeviceInputs;
	std::vector<RegisterSoftwareDeviceOutputs> _registerSoftwareDeviceOutputs;
	int _registerSoftwareDeviceOutputCount;

	std::vector<GetAdapterCountOutputs> _getAdapterCountOutputs;
	int _getAdapterCountOutputCount;

	std::vector<GetAdapterIdentifierInputs> _getAdapterIdentifierInputs;
	std::vector<GetAdapterIdentifierOutputs> _getAdapterIdentifierOutputs;
	int _getAdapterIdentifierOutputCount;

	std::vector<GetAdapterModeCountInputs> _getAdapterModeCountInputs;
	std::vector<GetAdapterModeCountOutputs> _getAdapterModeCountOutputs;
	int _getAdapterModeCountOutputCount;

	std::vector<EnumAdapterModesInputs> _enumAdapterModesInputs;
	std::vector<EnumAdapterModesOutputs> _enumAdapterModesOutputs;
	int _enumAdapterModesOutputCount;

	std::vector<GetAdapterDisplayModeInputs> _getAdapterDisplayModeInputs;
	std::vector<GetAdapterDisplayModeOutputs> _getAdapterDisplayModeOutputs;
	int _getAdapterDisplayModeOutputCount;

	std::vector<CheckDeviceTypeInputs> _checkDeviceTypeInputs;
	std::vector<CheckDeviceTypeOutputs> _checkDeviceTypeOutputs;
	int _checkDeviceTypeOutputCount;

	std::vector<CheckDeviceFormatInputs> _checkDeviceFormatInputs;
	std::vector<CheckDeviceFormatOutputs> _checkDeviceFormatOutputs;
	int _checkDeviceFormatOutputCount;

	std::vector<CheckDeviceMultiSampleTypeInputs> _checkDeviceMultiSampleTypeInputs;
	std::vector<CheckDeviceMultiSampleTypeOutputs> _checkDeviceMultiSampleTypeOutputs;
	int _checkDeviceMultiSampleTypeOutputCount;

	std::vector<CheckDepthStencilMatchInputs> _checkDepthStencilMatchInputs;
	std::vector<CheckDepthStencilMatchOutputs> _checkDepthStencilMatchOutputs;
	int _checkDepthStencilMatchOutputCount;

	std::vector<CheckDeviceFormatConversionInputs> _checkDeviceFormatConversionInputs;
	std::vector<CheckDeviceFormatConversionOutputs> _checkDeviceFormatConversionOutputs;
	int _checkDeviceFormatConversionOutputCount;

	std::vector<GetDeviceCapsInputs> _getDeviceCapsInputs;
	std::vector<GetDeviceCapsOutputs> _getDeviceCapsOutputs;
	int _getDeviceCapsOutputCount;

	std::vector<GetAdapterMonitorOutputs> _getAdapterMonitorOutputs;
	std::vector<GetAdapterMonitorInputs> _getAdapterMonitorInputs;
	int _getAdapterMonitorOutputCount;

	std::vector<CreateDeviceInputs> _createDeviceInputs;
	std::vector<CreateDeviceOutputs> _createDeviceOutputs;
	int _createDeviceOutputCount;
};
